#include "kernel.h"
#define NULL 0

/* Initializes the hashtable */
void hashtable_init(struct fingerprint_node* f[HASHTABLE_SIZE], struct ipSrc_node* s[HASHTABLE_SIZE], struct destPort_node* d[HASHTABLE_SIZE]){
	//Filling the arrays with NULL values
	for(int i=0; i<HASHTABLE_SIZE; i++){
		f[i]=NULL;
		d[i]=NULL;
		s[i]=NULL;
	}

}

/* Adds a fingerprint_node to the fingerprint_node hashtable */
void addFingerprint(struct fingerprint_node* h[HASHTABLE_SIZE],  struct node_packet *node, int* lock){
	mutex_lock(lock);

	//Hashing the value to get the index
	unsigned int packetLength= node->length;
	unsigned long rawIndex=djb2((unsigned char*) &(node->pkt), packetLength);//Need to figure out the second argument here
	int index= rawIndex % HASHTABLE_SIZE;
	struct fingerprint_node*  currNode= h[index];
	unsigned int success=1;
	
	//Checking and inserting into that particular array cell
	while(success){
		//If currNode is null then this array cell is empty, add node
		if(currNode==NULL){
			//Creating and initializing the new node
			struct fingerprint_node* newNode= (struct fingerprint_node*)malloc(sizeof(struct fingerprint_node));
			newNode->count=0;
			newNode->fingerprint=rawIndex;
			newNode->next=NULL;
			newNode->previous=NULL;
			h[index]=newNode;
			success=0;
		}else if(currNode->fingerprint==rawIndex){//If already in the list, do nothing
			success=0;
		}else if(currNode->next==NULL){//Reached the end of the linked list, add node
			struct fingerprint_node* newNode= (struct fingerprint_node*)malloc(sizeof(struct fingerprint_node));
			newNode->count=0;
			newNode->fingerprint=rawIndex;
			newNode->next=NULL;
			newNode->previous=currNode;
			currNode->next=newNode;
			success=0;
		}else{
			//Moving along the list
			currNode=currNode->next;
		}
	}
	
	free(node);
	mutex_unlock(lock);
}

/* Adds an ipSrc_node to the ipSrc_node hashtable */
void addIpSrc(struct ipSrc_node* h[HASHTABLE_SIZE],  struct node_packet *node, int* lock){
	mutex_lock(lock);
	unsigned char* valueToHash= (unsigned char *) &((node->pkt).headers).ip_source_address_big_endian;

	//Hashing the value to get the index
	unsigned long rawIndex=djb2(valueToHash, sizeof(int));//Need to figure out the second argument here
	int index= rawIndex % HASHTABLE_SIZE;
	struct ipSrc_node*  currNode= h[index];
	unsigned int success=1;
	
	//Checking and inserting into that particular array cell
	while(success){
		//If currNode is null then this array cell is empty, add node
		if(currNode==NULL){
			//Creating and initializing the new node
			struct ipSrc_node* newNode= (struct ipSrc_node*)malloc(sizeof(struct ipSrc_node));
			newNode->count=0;
			newNode->ipSrc=rawIndex;
			newNode->next=NULL;
			newNode->previous=NULL;
			h[index]=newNode;
			success=0;
		}else if(currNode->ipSrc==rawIndex){//If already in the list, do nothing
			success=0;
		}else if(currNode->next==NULL){//Reached the end of the linked list, add node
			struct ipSrc_node* newNode= (struct ipSrc_node*)malloc(sizeof(struct ipSrc_node));
			newNode->count=0;
			newNode->ipSrc=rawIndex;
			newNode->next=NULL;
			newNode->previous=currNode;
			currNode->next=newNode;
			success=0;
		}else{
			//Moving along the list
			currNode=currNode->next;
		}
	}
	
	free(node);
	mutex_unlock(lock);
}

/* Adds a destPort_node to the destPort_node hashtable */
void addDestPort(struct destPort_node* h[HASHTABLE_SIZE],  struct node_packet *node, int* lock){
	mutex_lock(lock);
	unsigned char* valueToHash=(unsigned char*) &((node->pkt).headers).udp_dest_port_big_endian;;
	
	//Hashing the value to get the index
	unsigned long rawIndex=djb2(valueToHash, sizeof(short));//Need to figure out the second argument here
	int index= rawIndex % HASHTABLE_SIZE;
	struct destPort_node*  currNode= h[index];
	unsigned int success=1;
	
	//Checking and inserting into that particular array cell
	while(success){
		//If currNode is null then this array cell is empty, add node
		if(currNode==NULL){
			//Creating and initializing the new node
			struct destPort_node* newNode= (struct destPort_node*)malloc(sizeof(struct destPort_node));
			newNode->count=0;
			newNode->destPort=rawIndex;
			newNode->next=NULL;
			newNode->previous=NULL;
			h[index]=newNode;
			success=0;
		}else if(currNode->destPort==rawIndex){//If already in the list, do nothing
			success=0;
		}else if(currNode->next==NULL){//Reached the end of the linked list, add node
			struct destPort_node* newNode= (struct destPort_node*)malloc(sizeof(struct destPort_node));
			newNode->count=0;
			newNode->destPort=rawIndex;
			newNode->next=NULL;
			newNode->previous=currNode;
			currNode->next=newNode;
			success=0;
		}else{
			//Moving along the list
			currNode=currNode->next;
		}
	}
	free(node);
	mutex_unlock(lock);
	
}

/* Removes the fingerprint_node from the fingerprint_node hashtable */
void removeFingerprint(struct fingerprint_node* h[HASHTABLE_SIZE], struct node_packet *node, int* lock){
	mutex_lock(lock);
	//Hashing the value to get the index
	unsigned int packetLength= node->length;
	unsigned long rawIndex=djb2((unsigned char *) &(node->pkt), packetLength);
	int index= rawIndex % HASHTABLE_SIZE;
	struct fingerprint_node* currNode= h[index];
	unsigned int success=1;

	//Checking and removing from that particular array cell
	while(success){
	
		//If currNode is NULL, the packet is not there
		if(currNode==NULL){
			success=0;
			printf_m("The node was not found and thus cannot be removed \n");
		}else if(currNode->fingerprint==rawIndex){
			printf_m("Node was found, removing now \n");
			
			//If previous is null, this is the first node in this array cell
			if(currNode->previous==NULL){
				printf_m("It WAS the first node in that cell array \n");
				h[index]=currNode->next;
				if(currNode->next!=NULL){
					(currNode->next)->previous=NULL;
				}
				free(currNode);
			}else{
				printf_m("It WAS NOT first node in that cell array \n");
				(currNode->previous)->next=currNode->next;
				(currNode->next)->previous=currNode->previous;
				free(currNode);
			}
			success=0;
			
		}else{
			currNode=currNode->next;
		}
	
	}
	free(node);
	mutex_unlock(lock);

}

/* Removes an ipSrc_node from the ipSrc_node hashtable */
void removeIpSrc(struct ipSrc_node* h[HASHTABLE_SIZE], struct node_packet *node, int* lock){
	mutex_lock(lock);
	unsigned char* valueToHash=(unsigned char*) &((node->pkt).headers).ip_source_address_big_endian;
	
	//Hashing the value to get the index
	unsigned int packetLength= node->length;//need to change this
	unsigned long rawIndex=djb2(valueToHash, sizeof(int));//Need to figure out the second argument here
	int index= rawIndex % HASHTABLE_SIZE;
	struct ipSrc_node*  currNode= h[index];
	unsigned int success=1;

	//Checking and removing from that particular array cell
	while(success){
	
		//If currNode is NULL, the packet is not there
		if(currNode==NULL){
			success=0;
			printf_m("The node was not found and thus cannot be removed \n");
		}else if(currNode->ipSrc==rawIndex){
			printf_m("Node was found, removing now \n");
			
			//If previous is null, this is the first node in this array cell
			if(currNode->previous==NULL){
				printf_m("It WAS the first node in that cell array \n");
				h[index]=currNode->next;
				
				if(currNode->next!=NULL){
					(currNode->next)->previous=NULL;
				}
				free(currNode);
			}else{
				printf_m("It WAS NOT first node in that cell array \n");
				(currNode->previous)->next=currNode->next;
				(currNode->next)->previous=currNode->previous;
				free(currNode);
			}
			success=0;
			
		}else{
			currNode=currNode->next;
		}
	
	}
	free(node);
	mutex_unlock(lock);

}

/* Removes the destPort_node from the destPort_node hashtable */
void removeDestPort(struct destPort_node* h[HASHTABLE_SIZE], struct node_packet *node, int* lock){
	mutex_lock(lock);
	unsigned char* valueToHash=(unsigned char*) &((node->pkt).headers).udp_dest_port_big_endian;;
	
	//Hashing the value to get the index
	unsigned int packetLength= node->length;//need to change this
	unsigned long rawIndex=djb2(valueToHash, sizeof(short));//Need to figure out the second argument here
	int index= rawIndex % HASHTABLE_SIZE;
	struct destPort_node*  currNode= h[index];
	unsigned int success=1;

	//Checking and removing from that particular array cell
	while(success){
	
		//If currNode is NULL, the packet is not there
		if(currNode==NULL){
			success=0;
			printf_m("The node was not found and thus cannot be removed \n");
		}else if(currNode->destPort==rawIndex){
			printf_m("Node was found, removing now \n");
			
			//If previous is null, this is the first node in this array cell
			if(currNode->previous==NULL){
				printf_m("It WAS the first node in that cell array \n");
				h[index]=currNode->next;
				if(currNode->next!=NULL){
					(currNode->next)->previous=NULL;//What if next is null?
				}
				free(currNode);
			}else{
				printf_m("It WAS NOT first node in that cell array \n");
				(currNode->previous)->next=currNode->next;
				(currNode->next)->previous=currNode->previous;
				free(currNode);
			}
			success=0;
			
		}else{
			currNode=currNode->next;
		}
	
	}
	free(node);
	mutex_unlock(lock);

}

/* Returns 1 if the hashtable contains the fingerprint_node, 0 otherwise */
int containsFingerprint(struct fingerprint_node* h[HASHTABLE_SIZE], struct node_packet *node, int* lock){
	mutex_lock(lock);
	//Hashing the value to get the index
	unsigned int packetLength= node->length;
	unsigned long rawIndex=djb2((char *) &(node->pkt), packetLength);
	int index= rawIndex % HASHTABLE_SIZE;
	struct fingerprint_node* currNode= h[index];
	
	//Checking that particular array cell
	while(1){
		//If currNode is NULL, the packet is not there
		if(currNode==NULL){
			printf_m("The node was not found! \n");
			mutex_unlock(lock);
			return 0;
		}else if(currNode->fingerprint==rawIndex){
			printf_m("Node was found! \n");
			currNode->count++;
			mutex_unlock(lock);
			return 1;
		}else{
			printf_m("Still searching... \n");
			currNode=currNode->next;
		}
	}
	mutex_unlock(lock);
	return 0;
}

/* Returns 1 if the hashtable contains the ipSrc_node, 0 otherwise */
int containsIpSrc(struct ipSrc_node* h[HASHTABLE_SIZE], struct node_packet *node, int* lock){
	mutex_lock(lock);
	unsigned char* valueToHash=(unsigned char*) &((node->pkt).headers).ip_source_address_big_endian;
	
	//Hashing the value to get the index
	unsigned long rawIndex=djb2((unsigned char *) valueToHash, sizeof(int));
	int index= rawIndex % HASHTABLE_SIZE;
	struct ipSrc_node*  currNode= h[index];
	unsigned int success=1;
	
	//Checking that particular array cell
	while(1){
		//If currNode is NULL, the packet is not there
		if(currNode==NULL){
			printf_m("The node was not found! \n");
			mutex_unlock(lock);
			return 0;
		}else if(currNode->ipSrc==rawIndex){
			printf_m("Node was found! \n");
			currNode->count++;
			mutex_unlock(lock);
			return 1;
		}else{
			printf_m("Still searching... \n");
			currNode=currNode->next;
		}
		
	}
	mutex_unlock(lock);
	return 0;
}

/* Returns 1 if the hashtable contains the destPort_node, 0 otherwise */
int containsDestPort(struct destPort_node* h[HASHTABLE_SIZE], struct node_packet *node, int* lock){
	mutex_lock(lock);
	unsigned char* valueToHash=(unsigned char*) &((node->pkt).headers).udp_dest_port_big_endian;;
	
	//Hashing the value to get the index
	unsigned int packetLength= node->length;//need to change this
	unsigned long rawIndex=djb2(valueToHash, sizeof(short));//Need to figure out the second argument here
	int index= rawIndex % HASHTABLE_SIZE;
	struct destPort_node*  currNode= h[index];
	unsigned int success=1;
	//int tempCount=0;
	//Checking that particular array cell
	while(1){
		
		//If currNode is NULL, the packet is not there
		if(currNode==NULL){
			mutex_unlock(lock);
			return 0;
		}else if(currNode->destPort==rawIndex){
			printf_m("Node was found! \n");
			currNode->count++;
			mutex_unlock(lock);
			return 1;
		}else{
			printf_m("Still searching... \n");
			currNode=currNode->next;
		}
	}
	mutex_unlock(lock);
	return 0;
}

/* Hash function provided by the course staff (http://www.cs.cornell.edu/Courses/CS3410/2012sp/faq/faq_pa4.html) */
unsigned long djb2(unsigned char *pkt, int n)
{
  unsigned long hash = 5381;
  int c;
  for (int i = 0; i < n; i++) {
    c = pkt[i];
    hash = hash * 33 + c;
    //hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
  }
  return hash;
}

void inc_processed(){
	packets_processed++;
}

void print_stats(struct fingerprint_node* f[HASHTABLE_SIZE], struct ipSrc_node* s[HASHTABLE_SIZE], struct destPort_node* d[HASHTABLE_SIZE], int* fLock, int* iLock, int* dLock){
	printf_m("packets proccessed: %d \n", packets_processed);
	mutex_lock(fLock);
	mutex_lock(iLock);
	mutex_lock(dLock);
	for(int i=0; i<HASHTABLE_SIZE; i++){
		if(f[i]){	
			printf_m("Evil packets found for hash %d : %d \n", i, f[i]->count);
		}
		if(d[i]){
			printf_m("Spammers found for has %d : %d \n",i,d[i]->count);
		}
		if(s[i]){
			printf_m("Vulnerable ports targeted %d : %d \n",i,s[i]->count);
		}
	}
	mutex_unlock(dLock);
	mutex_unlock(iLock);
	mutex_unlock(fLock);
	
	
}


